/*
 * Copyright 2019-2029 FISOK(www.fisok.cn).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.fisok.pdman.command.impl;

import cn.fisok.pdman.model.Entity;
import cn.fisok.pdman.model.Field;
import cn.fisok.pdman.model.Module;
import cn.fisok.raw.kit.FileKit;
import cn.fisok.raw.kit.IOKit;
import cn.fisok.raw.kit.StringKit;
import cn.fisok.raw.lang.MapObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author : 杨松<yangsong158@qq.com>
 * @date : 2020/9/4
 * @desc : 生成文档的抽象类
 */
public abstract class AbstractDocCommand {

    protected MapObject parseFile(String pdmanFile) throws IOException {
        MapObject mapObject = null;
        try(InputStream inputStream = FileKit.openInputStream(new File(pdmanFile))) {
            String jsonText = IOKit.toString(inputStream, Charset.defaultCharset());
            mapObject = MapObject.valueOf(jsonText);
        } catch (IOException e) {
            throw e;
        }
        return mapObject;
    }

    /**
     * 生成模块列表
     * @param mapObject
     * @return
     */
    public List<Module> parseModules(MapObject mapObject){
        List<Module> modules = new ArrayList<>();

        final List<MapObject> domainMoList =  MapObject.valueOfBeanArray(mapObject.getValue("dataTypeDomains.datatype").objectArray());
        final List<MapObject> databaseMoList = MapObject.valueOfBeanArray(mapObject.getValue("dataTypeDomains.database").objectArray());
        String fileShowTypeDb = lookupfileShowDbType(databaseMoList);
        Map<String,String> domainDbTypeMap = parseDomainToDbTypeMap(domainMoList,fileShowTypeDb);

        final List<MapObject> modulesMoList = MapObject.valueOfBeanArray(mapObject.getValue("modules").objectArray());

        for(MapObject moModule:modulesMoList){
            modules.add(parseModule(moModule,domainDbTypeMap));
        }

        return modules;
    }

    /**
     * 查找生成至文档需要的默认数据库类型
     * @param databaseMoList
     * @return
     */
    public String lookupfileShowDbType(List<MapObject> databaseMoList){
        for(MapObject moDbType : databaseMoList){
            if(moDbType.getValue("defaultDatabase").boolValue(false)){
                return moDbType.getValue("code").strValue("MYSQL");
            }
        }
        return "MYSQL";
    }

    public Map<String,String> parseDomainToDbTypeMap(List<MapObject> domainMoList,String dbType){
        Map<String,String> domainDbTypeMap = new HashMap<>();

        for(MapObject moDomain : domainMoList){
            String domainCode = moDomain.getValue("code").strValue("");
            if(StringKit.isBlank(domainCode))continue;
            String domainDbRealType = moDomain.getValue("apply."+dbType+".type").strValue("");
            domainDbTypeMap.put(domainCode,domainDbRealType);
        }


        return domainDbTypeMap;
    }

    public Module parseModule(MapObject moModule,Map<String,String> domainDbTypeMap){
        Module module = new Module();
        List<Entity> entities = new ArrayList<>();
        module.setEntities(entities);

        String name = moModule.getValue("name").strValue("UNNAME_MODULE");
        String key = name;
        String label = moModule.getValue("chnname").strValue("");
        int  delimiterIdx = key.lastIndexOf('-');
        if(StringKit.isBlank(label) && delimiterIdx > 0){
            key = name.substring(0,delimiterIdx);
            label = name.substring(delimiterIdx+1);
        }

        module.setKey(key);
        module.setLabel(label);

        final List<MapObject> moEntities = MapObject.valueOfBeanArray(moModule.getValue("entities").objectArray());
        final List<MapObject> moAssocList = MapObject.valueOfBeanArray(moModule.getValue("associations").objectArray());
        final Map<String,String> entityReferToMap = parseEntityReferToMap(moAssocList);

        if(moEntities != null){
            for(MapObject moEntity:moEntities){
                entities.add(parseEntity(moEntity,domainDbTypeMap,entityReferToMap));
            }
        }

        //刷新计算一下顺序号
        for(int i=0;i<entities.size();i++){
            entities.get(i).setIdx(i+1);
            List<Field> fields = entities.get(i).getFields();
            for(int j=0;j<fields.size();j++){
                fields.get(j).setIdx(j+1);
            }
        }

        return module;
    }


    /**
     * 实体之间的关联关系,KEY为:源表.字段，VALUE为：目标表.字段
     * @return
     */
    public Map<String,String> parseEntityReferToMap(List<MapObject> moAssocList){
        Map<String,String> referToMap = new HashMap<>();

        if(moAssocList == null || moAssocList.size() == 0)return referToMap;
        for(MapObject moAssoc : moAssocList){
            String from = moAssoc.getValue("from.entity").strValue("")+"."+moAssoc.getValue("from.field").strValue("");
            String to = moAssoc.getValue("to.entity").strValue("")+"."+moAssoc.getValue("to.field").strValue("");
            referToMap.put(from,to);
        }

        return referToMap;
    }

    public Entity parseEntity(MapObject moEntity,Map<String,String> domainDbTypeMap,Map<String,String> entityReferToMap){
        Entity entity = new Entity();
        List<Field> fields = new ArrayList<Field>();
        entity.setFields(fields);

        entity.setKey(moEntity.getValue("title").strValue("UNNAME_ENTITY"));
        entity.setLabel(moEntity.getValue("chnname").strValue("未命名表"));
        entity.setRemark(moEntity.getValue("remark").strValue(""));

        final List<MapObject> moFields = MapObject.valueOfBeanArray(moEntity.getValue("fields").objectArray());
        if(moFields != null){
            for(MapObject moField:moFields){
                fields.add(parseField(moField,entity,domainDbTypeMap,entityReferToMap));
            }
        }

        return entity;
    }

    public Field parseField(MapObject moField,Entity entity,Map<String,String> domainDbTypeMap,Map<String,String> entityReferToMap){
        Field field = new Field();

        String entityKey = entity.getKey();

        field.setKey(moField.getValue("name").strValue("UNNAME_FIELD"));
        field.setLabel(moField.getValue("chnname").strValue(""));
        field.setRemark(moField.getValue("remark").strValue(""));
        field.setPk(moField.getValue("pk").boolValue(false)?"√":"");
        field.setNotNull(moField.getValue("notNull").boolValue(false)?"√":"");
        field.setDomainType(moField.getValue("type").strValue(""));
        field.setAutoInc(moField.getValue("autoIncrement").boolValue(false)?"√":"");
        field.setDefaultValue(moField.getValue("defaultValue").strValue(""));

        //计算数据类型以及外表依赖
        String dbType = StringKit.nvl(domainDbTypeMap.get(field.getDomainType()),"");
        String referMapKey = entityKey+"."+field.getKey();
        String referTo = StringKit.nvl(entityReferToMap.get(referMapKey),"");

        field.setDbType(dbType);
        field.setReferTo(referTo);

        return field;
    }
}
